package zd

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"

	"gitee.com/chunanyong/zorm"
	"github.com/pkg/errors"
)

// PageInfo 分页数据
type PageInfo struct {
	PageNo     int `json:"pageNo,omitempty"`
	PageSize   int `json:"pageSize,omitempty"`
	TotalCount int `json:"totalCount,omitempty"`
}

// valToSql 将值按类型转为 sql 语句的值
//
//	@param v
//	@return any
func valToSql(v any) any {
	switch v := v.(type) {
	case string, int, float64, []any, []string, []int, []float64, []bool, []time.Time: // 数组
		return v
	case bool:
		if v {
			return "1"
		} else {
			return "0"
		}
	}
	// 处理类型别名
	var val any
	if newVal, _ := json.Marshal(v); bytes.Contains(newVal, []byte("\"")) { // 有 " 表示内容为 基于 string的自定义类型
		val = fmt.Sprintf("%s", v)
	} else if n, err := strconv.ParseInt(string(newVal), 0, 8); err == nil { // byte
		val = fmt.Sprintf("%c", n) //  fmt.Sprintf("%s", v):: TSNew->"New"
	} else if n, err := strconv.Atoi(string(newVal)); err == nil { // int
		val = fmt.Sprintf("%d", n)
	} else if n, err := strconv.ParseFloat(string(newVal), 64); err == nil { // float64
		val = fmt.Sprintf("%f", n)
	} else if b, err := strconv.ParseBool(string(newVal)); err == nil { // bool 别名
		if b {
			val = "1"
		} else {
			val = "0"
		}
	} else {
		val = v
	}
	return val
}

// mapToFinder map 生成 finder(自定义类型转换 byte -> char)
//
//	@param params map[string]any
//	@param joinStr AND 或者 ,
//	@return *zorm.Finder
func mapToFinder(params map[string]any, joinStr string) *zorm.Finder {
	if len(params) == 0 {
		return zorm.NewFinder().Append("1=1")
	}
	var vals = make([]any, 0)
	var keys = make([]string, 0)
	for k, v := range params {
		if v == nil {
			if joinStr == "AND" { // 处理 where 中的 NULL(nil)
				// keys = append(keys, fmt.Sprintf("ISNULL(%s) = ?", k))
				// vals = append(vals, true)
				keys = append(keys, k+" is NULL")
			} else {
				keys = append(keys, k+" = NULL")
			}
			continue
		}

		switch v := v.(type) {
		case map[string]any: // 实现 between .. and .. 以及 >= < != 等操作
			for kSub, vSub := range v {
				if strings.EqualFold(kSub, "between") { // between vSub 为数组
					// and 会被 fnd 替换成 ,
					keys = append(keys, k+" between ? and ?")
					vals = append(vals, vSub.([]any)...)
				} else if strings.EqualFold(kSub, "not in") { // not in vSub 为数组
					keys = append(keys, k+" not in (?)")
					vals = append(vals, vSub)
				} else {
					keys = append(keys, fmt.Sprintf("%s %s ?", k, kSub))
					vals = append(vals, valToSql(vSub))
				}
			}
		case []any, []string, []int, []float64, []bool, []time.Time: // 数组
			keys = append(keys, k+" in (?)")
			vals = append(vals, v)
		case bool:
			keys = append(keys, k+" = ?")
			if v {
				vals = append(vals, "1")
			} else {
				vals = append(vals, "0")
			}
		case string, int, float64:
			keys = append(keys, k+" = ?")
			vals = append(vals, v)
		default: // 自定义类型别名
			kind := reflect.TypeOf(v).Kind()
			switch kind {
			case reflect.Slice: // 自定义类型的数组: []InstrumentIDType
				keys = append(keys, k+" in (?)")
				vals = append(vals, v)
			case reflect.Int, reflect.Int32, reflect.Int64, reflect.Float64, reflect.Float32, reflect.String:
				keys = append(keys, k+" = ?")
				vals = append(vals, v)
			case reflect.Bool:
				keys = append(keys, k+" = ?")
				if b, _ := strconv.ParseBool(fmt.Sprintf("%v", v)); b {
					vals = append(vals, "1")
				} else {
					vals = append(vals, "0")
				}
			case reflect.Int8: // byte
				keys = append(keys, k+" = ?")
				vals = append(vals, fmt.Sprintf("%c", v.(byte)))
			default:
				keys = append(keys, k+" = ?")
				vals = append(vals, valToSql(v))
			}
		}
	}
	key := strings.Join(keys, " "+joinStr+" ")
	fnd := zorm.NewFinder()
	fnd.InjectionCheck = false
	return fnd.Append(key, vals...)
}

// mapToWhereFinder 用以 where update 操作
//
//	@param params map[string]any 参数
//	@return *zorm.Finder 查询
func mapToWhereFinder(params map[string]any) *zorm.Finder {
	return mapToFinder(params, "AND")
}

// mapToSetFinder 用于 update
//
//	@param params map[string]any 参数
//	@return *zorm.Finder 查询
func mapToSetFinder(params map[string]any) *zorm.Finder {
	return mapToFinder(params, ",")
}

// getFinder
//
//	@param ctx
//	@param entity
//	@param p
//	@param where
//	@param columns
//	@param sqlStrAppend
//	@return *zorm.Page
//	@return *zorm.Finder
func getFinder(ctx context.Context, entity IEntity, p *PageInfo, where map[string]any, columns []string, sqlStrAppend ...string) (*zorm.Page, *zorm.Finder) {
	cols := GetColumns(ctx, entity)
	// where 修正为数据库字段
	// 有适配的 key, 则替换为数据库字段名, 否则保留
	for _, col := range cols {
		if v, ok := where[col.StructFieldName]; ok {
			delete(where, col.StructFieldName)
			where[col.ColumnName] = v
		}
		// 转换列名
		for i := range columns {
			columns[i] = strings.ReplaceAll(columns[i], col.StructFieldName, col.ColumnName)
		}
		// 转换 sqlAppend 中的字段名
		for i := range sqlStrAppend {
			sqlStrAppend[i] = strings.ReplaceAll(sqlStrAppend[i], col.StructFieldName, col.ColumnName)
		}
	}
	return getFinderByName(ctx, GetTableName(ctx, entity), p, where, columns, sqlStrAppend...)
}

// getFinderByName 生成 finder
//
//	@param ctx
//	@param tblName string 表名
//	@param p *PageInfo 分页
//	@param where map[string]any 查询条件
//	@param columns []string 查询列
//	@param sqlStrAppend ...string []string sql拓展, 如 order by xxx, 不会被 fnd 替换
//	@return *zorm.Page 分页信息
//	@return *zorm.Finder finder
func getFinderByName(ctx context.Context, tblName string, p *PageInfo, where map[string]any, columns []string, sqlStrAppend ...string) (*zorm.Page, *zorm.Finder) {
	//构造查询用的fnd
	if !strings.Contains(tblName, ".") { // 不带 schema 的用默认 schemaName
		tblName = schemaName(ctx) + "." + tblName
	}
	var fnd = zorm.NewSelectFinder(tblName) // select * from t_demo
	if len(columns) > 0 {
		cols := make([]string, len(columns)) // 转为大写,以适应列名
		for i, v := range columns {
			cols[i] = strings.ToUpper(v)
		}
		fnd = zorm.NewSelectFinder(tblName, strings.Join(cols, ",")) // select * from t_demo
	}
	if len(where) > 0 { // 只处理 where xx=? 的情况
		fnd.Append("WHERE").AppendFinder(mapToWhereFinder(where))
	} else {
		fnd.Append("WHERE 1=1")
	}
	if len(sqlStrAppend) > 0 && len(sqlStrAppend[0]) > 0 { // 拓展查询条件
		fndAppend := zorm.NewFinder()
		for _, v := range sqlStrAppend {
			fndAppend = fndAppend.Append(v)
		}
		fndAppend.InjectionCheck = false
		fndAppend.GetSQL() // 以 isActive='0' 附加查询条件时, 这样才能使 fndAppend 生效
		fnd.AppendFinder(fndAppend)
	}
	fnd.InjectionCheck = false
	fnd.GetSQL() // fnd 生效
	//创建分页对象,查询完成后,page对象可以直接给前端分页组件使用
	var page *zorm.Page
	if p != nil && p.PageSize > 0 { // 避免出现 除数为0的情况
		if p.PageNo == 0 { // 避免无返回的情况
			p.PageNo = 1
		}
		page = &zorm.Page{
			PageNo:   p.PageNo,   //查询第1页,默认是1
			PageSize: p.PageSize, //每页20条,默认是20
		}
	}
	return page, fnd
}

// Transaction 使用事务
//
//	@param ctx
//	@param fun func() error 使用了什么代码就抛出什么错误
//	@return error 错误
func Transaction(ctx context.Context, fun func(ctx context.Context) error) error {
	_, err := zorm.Transaction(ctx, func(ctx context.Context) (any, error) {
		return nil, fun(ctx)
	})
	return err
}

// SelectCount 根据指定条件统计记录数。
// 该函数使用了泛型参数 T，允许传入任何实现了 IEntity 接口的实体类型。
// 参数:
//
//	ctx: context.Context - 上下文，用于传递请求范围的上下文信息。
//	where: map[string]any - 查询条件，以键值对形式提供。
//	entities: ...T - 可变参数，提供一个或多个实体实例，用于推断实体类型。
//
// 返回值:
//
//	int - 符合查询条件的记录数。
//	error - 如果查询过程中发生错误，返回错误信息。
func SelectCount[T IEntity](ctx context.Context, where map[string]any, entities ...T) (int, error) {
	// 初始化实体变量，根据传入的实体参数决定使用哪个实体类型。
	var entity T
	if len(entities) == 0 {
		// 如果没有提供实体实例，则使用泛型参数 T 创建一个新的实体实例。
		entity = *new(T)
	} else {
		// 如果提供了实体实例，则使用第一个实体实例。
		entity = entities[0]
	}

	// 准备查询条件，但不执行查询。
	_, fnd := getFinder(ctx, entity, nil, where, []string{"Count(1)"})

	// 定义一个变量以存储查询结果。
	var cnt float64

	// 执行查询并将结果存储到 cnt 变量中。
	_, err := zorm.QueryRow(ctx, fnd, &cnt)
	if err != nil {
		// 如果查询过程中发生错误，返回错误信息。
		return -1, err
	}

	// 将查询结果转换为 int 类型并返回。
	return int(cnt), nil
}

// SelectCountByName 根据表名查询 count(1)
//
//	@param ctx
//	@param tblName string 表名
//	@param where map[string]any 查询条件
//	@return int count(1)
//	@return error 错误
func SelectCountByName(ctx context.Context, tblName string, where map[string]any) (int, error) {
	_, fnd := getFinderByName(ctx, tblName, nil, where, []string{"Count(1)"}, "")
	var cnt float64
	_, err := zorm.QueryRow(ctx, fnd, &cnt)
	if err != nil {
		return -1, err
	}
	return int(cnt), nil
}

func SelectRow[T IEntity](ctx context.Context, columnName string, where map[string]any, entities ...T) (any, error) {
	var entity T
	if len(entities) == 0 {
		entity = *new(T)
	} else {
		entity = entities[0]
	}
	tblName := GetTableName(ctx, entity)
	return SelectRowByName(ctx, tblName, columnName, where)
}

// SelectRow 查询首行指定列的值
//
//	@param ctx
//	@param entity IEntity 实体
//	@param columnName string 列名
//	@param where map[string]any 查询条件
//	@param sqlStrAppend ...string sql拓展, 如 order by xxx, 不会被 fnd 替换
//	@return any 无效查询返回  nil
//	@return error
func SelectRowWithAppend[T IEntity](ctx context.Context, columnName string, where map[string]any, sqlStrAppend string, entities ...T) (any, error) {
	var entity T
	if len(entities) == 0 {
		entity = *new(T)
	} else {
		entity = entities[0]
	}
	tblName := GetTableName(ctx, entity)
	return SelectRowByName(ctx, tblName, columnName, where, sqlStrAppend)
}

// SelectRowByName 查询首行指定列的值
//
//	@param ctx
//	@param tblName string 表名
//	@param columnName []string 列名
//	@param params map[string]any 查询条件
//	@param sqlStrAppend ...string sql拓展, 如 order by xxx, 不会被 fnd 替换
//	@return any 无效查询返回 nil
//	@return error
func SelectRowByName(ctx context.Context, tblName string, columnName string, params map[string]any, sqlStrAppend ...string) (any, error) {
	rows, err := SelectMapColumnsByName(ctx, tblName, nil, params, []string{columnName}, sqlStrAppend...)
	if err != nil {
		return nil, err
	}
	if len(rows) == 0 {
		return nil, nil
	}
	for _, v := range rows[0] {
		return v, nil
	}
	return nil, nil
}

// Select 查询, 返回 struct
//
//	@param ctx
//	@param entity IEntity 实体
//	@param p *PageInfo 分页
//	@param where map[string]any 查询条件
//	@param sqlStrAppend ...string sql拓展, 如 order by xxx, 不会被 fnd 替换
//	@return []map[string]any 返回结果集
//	@return error 错误
func Select[T IEntity](ctx context.Context, p *PageInfo, where map[string]any, sqlStrAppend ...string) ([]T, error) {
	page, fnd := getFinder(ctx, *new(T), p, where, nil, sqlStrAppend...)
	es := make([]T, 0)
	err := zorm.Query(ctx, fnd, &es, page)
	if err != nil {
		return nil, err
	}
	// 返回总数
	if p != nil {
		p.TotalCount = page.TotalCount
	}
	return es, nil
}

// SelectMap 返回 map 指定列名
//
//	@param ctx
//	@param entity IEntity 实体
//	@param p *PageInfo 分页
//	@param where map[string]any 查询条件
//	@param sqlStrAppend ...string sql拓展, 如 order by xxx, 不会被 fnd 替换
//	@return []map[string]any 返回结果集
//	@return error 错误
func SelectMap[T IEntity](ctx context.Context, p *PageInfo, where map[string]any, entities ...T) ([]map[string]any, error) {
	if len(entities) == 0 {
		return SelectMapWithAppend[T](ctx, p, where, "")
	}
	return SelectMapWithAppend(ctx, p, where, "", entities[0])
}

func SelectMapWithAppend[T IEntity](ctx context.Context, p *PageInfo, where map[string]any, sqlAppend string, entities ...T) ([]map[string]any, error) {
	// 泛型模式
	if len(entities) == 0 {
		ts, err := Select[T](ctx, p, where, sqlAppend)
		if err != nil {
			return nil, err
		}
		data := make([]map[string]any, len(ts))
		for i, t := range ts {
			data[i] = StructToMap(ctx, t, false, false)
		}
		return data, nil
	}
	// 实例模式
	entity := entities[0]
	page, fnd := getFinder(ctx, entity, p, where, nil, sqlAppend)
	rows, err := zorm.QueryMap(ctx, fnd, page)
	if err != nil {
		return nil, err
	}
	// 返回总数
	if p != nil {
		p.TotalCount = page.TotalCount
	}
	// 修正返回结果集的 Key
	fixMapToStruct(ctx, entity, rows...)
	return rows, nil
}

// SelectMapColumns SelectMap 返回 map 指定列名
//
//	@param ctx
//	@param entity IEntity 实体
//	@param p *PageInfo 分页
//	@param where map[string]any 查询条件
//	@param columns []string 指定列名(可用字段名,或 max(XX))
//	@param sqlStrAppend ...string sql拓展, 如 order by xxx, 不会被 fnd 替换
//	@return []map[string]any 返回结果集
//	@return error 错误
func SelectMapColumns[T IEntity](ctx context.Context, p *PageInfo, where map[string]any, columns []string, entities ...T) ([]map[string]any, error) {
	if len(entities) == 0 {
		return SelectMapColumnsWithAppend[T](ctx, p, where, columns, "")
	}
	return SelectMapColumnsWithAppend(ctx, p, where, columns, "", entities[0])
}

// SelectMapColumns SelectMap 返回 map 指定列名
//
//	@param ctx
//	@param entity IEntity 实体
//	@param p *PageInfo 分页
//	@param where map[string]any 查询条件
//	@param columns []string 指定列名(可用字段名,或 max(XX))
//	@param sqlStrAppend ...string sql拓展, 如 order by xxx, 不会被 fnd 替换
//	@return []map[string]any 返回结果集
//	@return error 错误
func SelectMapColumnsWithAppend[T IEntity](ctx context.Context, p *PageInfo, where map[string]any, columns []string, sqlStrAppend string, entities ...T) ([]map[string]any, error) {
	var entity T
	if len(entities) == 0 {
		entity = *new(T)
	} else {
		entity = entities[0]
	}
	page, fnd := getFinder(ctx, entity, p, where, columns, sqlStrAppend)
	rows, err := zorm.QueryMap(ctx, fnd, page)
	if err != nil {
		return nil, err
	}

	if p != nil {
		p.TotalCount = page.TotalCount
	}
	// 如果没有数据, 直接返回
	if len(rows) == 0 {
		return rows, nil
	}
	fixMapToStruct(ctx, entity, rows...)
	return rows, err
}

// SelectMapColumnsByName
// SelectMapColumnsByName 根据表名查询, 返回 map 指定列名
//
//	@param ctx
//	@param tblName string 表名
//	@param p *PageInfo 分页
//	@param where map[string]any 查询条件
//	@param columns []string 查询列
//	@param sqlStrAppend ...string sql拓展, 如 order by xxx, 不会被 fnd 替换
//	@return []map[string]any 返回结果集
//	@return error 错误
func SelectMapColumnsByName(ctx context.Context, tblName string, p *PageInfo, where map[string]any, columns []string, sqlStrAppend ...string) ([]map[string]any, error) {
	page, fnd := getFinderByName(ctx, tblName, p, where, columns, sqlStrAppend...)
	rows, err := zorm.QueryMap(ctx, fnd, page)
	if p != nil {
		p.TotalCount = page.TotalCount
	}
	return rows, err
}

// SelectByStruct 查询,条件用 IEntity 实例, 返回 struct
//
//	@param ctx
//	@param p *PageInfo 分页
//	@param t T 只接受非0值, 若查查询 bool:false int:0 string:"" 的条件,可在 sqlStrAppend 拼接
//	@param sqlStrAppend ...string sql拓展, 如 order by xxx, 不会被 fnd 替换
//	@return []T 返回结果集
//	@return error 错误
func SelectByStruct[T IEntity](ctx context.Context, p *PageInfo, t T, sqlStrAppend ...string) ([]T, error) {
	where := StructToMap(ctx, t, true, false)
	return Select[T](ctx, p, where, sqlStrAppend...)
}

// Insert 插入数据(不要用指针类型!!!不要用指针类型!!!不要用指针类型!!!)
//
//	@param ctx context.Context
//	@param filterZeroField bool 是否过滤空值[推荐:false](string:"",int:0,bool:false)
//	@param entities ...T 插入数据
//	@return n int 成功数量
//	@return err
func Insert[T IEntity](ctx context.Context, filterZeroField bool, entities ...T) (n int, err error) {
	entity := entities[0]
	Transaction(ctx, func(ctx context.Context) error {
		if ctx.Value(PG) == nil { // 非 postgres
			// 有自增字段时先执行 SET IDENTITY_INSERT %s ON
			for _, col := range GetColumns(ctx, entity) {
				if col.AutoIncrement {
					zorm.UpdateFinder(ctx, zorm.NewFinder().Append(fmt.Sprintf("SET IDENTITY_INSERT %s ON", GetTableName(ctx, entity))))
					break
				}
			}
		}
		entityMaps := make([]zorm.IEntityMap, len(entities))
		for i, entity := range entities {
			// 不过滤空值: struct 初始化的字段也会入库, 如 string 字段会以*空白字串*入库
			em := zorm.NewEntityMap(GetTableName(ctx, entity))
			for k, v := range fixMapToDBName(ctx, entity, StructToMap(ctx, entity, filterZeroField, true)) {
				// byte 自定义数据类型
				if reflect.TypeOf(v).Kind() == reflect.Uint8 && reflect.TypeOf(v).Name() != "uint8" {
					em.Set(k, fmt.Sprintf("%c", v))
					continue
				}
				em.Set(k, v)
			}
			entityMaps[i] = em
		}
		n, err = zorm.InsertEntityMapSlice(ctx, entityMaps)
		return err
	})
	return
}

// InsertMap 插入数据
//
//	@param ctx
//	@param entity IEntity 实体
//	@param m map[string]any 插入数据
//	@return n 插入数量
//	@return er
func InsertMap[T IEntity](ctx context.Context, m map[string]any, entities ...T) (n int, err error) {
	if len(entities) == 0 {
		return InsertMaps[T](ctx, []map[string]any{m})
	}
	return InsertMaps(ctx, []map[string]any{m}, entities[0])
}

// InsertMap 插入数据
//
//	@param ctx
//	@param entity IEntity 实体
//	@param mm []map[string]any 插入数据
//	@return n 插入数量
//	@return err
func InsertMaps[T IEntity](ctx context.Context, mm []map[string]any, entities ...T) (n int, err error) {
	var entity T
	if len(entities) == 0 {
		entity = *new(T)
	} else {
		entity = entities[0]
	}
	Transaction(ctx, func(ctx context.Context) error {
		entityMaps := make([]zorm.IEntityMap, len(mm))
		for i, m := range mm {
			// 不过滤空值: struct 初始化的字段也会入库, 如 string 字段会以*空白字串*入库
			em := zorm.NewEntityMap(GetTableName(ctx, entity))
			for k, v := range fixMapToDBName(ctx, entity, m) {
				if v == nil { // 过滤 nil 的值
					continue
				}
				// byte 自定义数据类型
				if reflect.TypeOf(v).Kind() == reflect.Uint8 && reflect.TypeOf(v).Name() != "uint8" {
					em.Set(k, fmt.Sprintf("%c", v))
					continue
				}
				em.Set(k, v)
			}
			entityMaps[i] = em
		}
		n, err = zorm.InsertEntityMapSlice(ctx, entityMaps)
		return err
	})
	return
}

// InsertMapByName 根据表名插入数据
//
//	@param ctx
//	@param tblName 表名
//	@param entities 插入数据
//	@return error 错误
func InsertMapByName(ctx context.Context, tblName string, mps ...map[string]any) error {
	return Transaction(ctx, func(ctx context.Context) error {
		if !strings.Contains(tblName, ".") { // 补充 schema
			tblName = schemaName(ctx) + "." + tblName
		}

		entitySlice := make([]zorm.IEntityMap, len(mps))
		for i, mp := range mps {
			entityMap := zorm.NewEntityMap(fixColumnName(ctx, tblName)) // tblName 转换后使用
			entityMap.PkColumnName = ""                                 //不写此行也可以 但是会打印log
			// entity = ToUpperKey(ctx, entity)
			for k, v := range mp {
				if v == nil { // 过滤 nil值, 入库数据会变为 NULL
					continue
				}
				k = fixColumnName(ctx, k) // columnName 转换后使用
				// byte 自定义数据类型
				if reflect.TypeOf(v).Kind() == reflect.Uint8 && reflect.TypeOf(v).Name() != "uint8" {
					entityMap.Set(k, fmt.Sprintf("%c", v))
					continue
				}
				entityMap.Set(k, v)
			}
			entitySlice[i] = entityMap
		}
		_, err := zorm.InsertEntityMapSlice(ctx, entitySlice)
		return err
	})
}

// Update 更新 Struct
//
//	@param ctx
//	@param entity IEntity 实体
//	@param entities []T 替换数据(**不能是指针! 不能是指针! 不能是指针!**)
//	@param filterZero 是否过滤零值(会影响 int:0, bool:false, string: "")
//	@return n int 成功数量
//	@return err error
func Update[T IEntity](ctx context.Context, entity T, filterZero bool) (n int, err error) {
	tblKeys := GetPrimaryKey(ctx, entity)
	err = Transaction(ctx, func(ctx context.Context) (err error) {
		where := make(map[string]any)
		set := fixMapToDBName(ctx, entity, StructToMap(ctx, entity, filterZero, true)) // 不过滤0值, 用于提取主键
		for _, k := range tblKeys {
			if tmp, ok := set[k]; !ok {
				return errors.New("缺少主键: " + k)
			} else {
				where[k] = tmp
			}
			delete(set, k) // 留下更新的部分
		}
		// 不要用 UpdateMap , 因多次使用 fixMap 导致数据错误
		finder := zorm.NewUpdateFinder(GetTableName(ctx, entity)) // UPDATE t_demo SET
		finder.AppendFinder(mapToSetFinder(set))
		finder.Append("WHERE").AppendFinder(mapToWhereFinder(where))
		finder.InjectionCheck = false
		finder.GetSQL()
		n, err = zorm.UpdateFinder(ctx, finder)
		if err != nil {
			return
		}
		return
	})
	return
}

// UpdateAndInsert 批量更新或插入(用于导入数据)
//
//	@param ctx
//	@param entity IEntity 实体
//	@param entities
//	@return n
//	@return err
func UpdateAndInsert[T IEntity](ctx context.Context, mps []map[string]any, entities ...T) (n int, err error) {
	var entity T
	if len(entities) == 0 {
		entity = *new(T)
	} else {
		entity = entities[0]
	}
	tblKeys := GetPrimaryKey(ctx, entity)
	err = Transaction(ctx, func(ctx context.Context) (err error) {
		if len(tblKeys) == 0 {
			return errors.New("没有主键对应的数据")
		}
		insertEntities := make([]map[string]any, 0)
		for _, mp := range mps {
			mp := fixMapToDBName(ctx, entity, mp) // 与数据库匹配的 key
			where := make(map[string]any)
			for _, k := range tblKeys {
				if tmp, ok := mp[k]; !ok {
					return errors.New("缺少主键: " + k)
				} else {
					where[k] = tmp
				}
			}
			// zorm.Update(ctx,zorm.IEntityStruct(entities[0])) // 只支持单字段 PrimaryKey, 不方便使用
			cnt, _ := SelectCount(ctx, where, entity)
			if cnt == 0 { // 新数据
				insertEntities = append(insertEntities, mp)
				continue
			}
			var nn int
			for k := range where {
				delete(mp, k) // 留下更新的部分
			}
			nn, err = UpdateMap(ctx, where, mp, entity)
			if err != nil {
				return
			}
			n += nn
		}
		if len(insertEntities) > 0 {
			nn, err := InsertMaps(ctx, insertEntities, entity)
			if err != nil {
				return errors.Wrap(err, "insert")
			}
			n += nn
		}
		return
	})
	if err != nil {
		n = 0
	}
	return
}

// UpdateMap 更新
//
//	@param ctx
//	@param entity IEntity 实体
//	@param where map[string]any 查询条件
//	@param set map[string]any 替换数据
//	@return int 替换数量
//	@return error 错误
func UpdateMap[T IEntity](ctx context.Context, where map[string]any, set map[string]any, entities ...T) (n int, err error) {
	var entity T
	if len(entities) == 0 {
		entity = *new(T)
	} else {
		entity = entities[0]
	}
	err = Transaction(ctx, func(ctx context.Context) (err error) {
		tblName := GetTableName(ctx, entity)
		where = fixMapToDBName(ctx, entity, where)
		set = fixMapToDBName(ctx, entity, set)
		finder := zorm.NewUpdateFinder(tblName) // UPDATE t_demo SET
		finder.AppendFinder(mapToSetFinder(set))
		finder.Append("WHERE").AppendFinder(mapToWhereFinder(where))
		finder.InjectionCheck = false
		finder.GetSQL()
		n, err = zorm.UpdateFinder(ctx, finder)
		return
	})
	return
}

// UpdateMapByName 根据表名更新
//
//	@param ctx
//	@param tblName string 表名
//	@param where map[string]any 查询条件
//	@param set map[string]any 替换数据
//	@return n
//	@return err
func UpdateMapByName(ctx context.Context, tblName string, where map[string]any, set map[string]any) (n int, err error) {
	Transaction(ctx, func(ctx context.Context) error {
		if !strings.Contains(tblName, ".") { // 补充 schema
			tblName = schemaName(ctx) + "." + tblName
		}
		finder := zorm.NewUpdateFinder(fixColumnName(ctx, tblName)) // UPDATE t_demo SET
		for k, v := range where {
			delete(where, k)
			where[fixColumnName(ctx, k)] = v
		}
		for k, v := range set {
			delete(set, k)
			set[fixColumnName(ctx, k)] = v
		}
		finder.AppendFinder(mapToSetFinder(set))
		finder.Append("WHERE").AppendFinder(mapToWhereFinder(where))
		finder.InjectionCheck = false
		finder.GetSQL()
		n, err = zorm.UpdateFinder(ctx, finder)
		// if n == 0 {
		// 	return fmt.Errorf("没有符合条件的数据: %v", where)
		// }
		return err
	})
	return
}

// Delete 删除
//
//	@param ctx
//	@param entity IEntity 实体
//	@param where map[string]any 删除条件
//	@return int 删除数量
//	@return error 错误
func Delete[T IEntity](ctx context.Context, where map[string]any, entities ...T) (int, error) {
	var entity T
	if len(entities) == 0 {
		entity = *new(T)
	} else {
		entity = entities[0]
	}
	tblName := GetTableName(ctx, entity)
	where = fixMapToDBName(ctx, entity, where) // 修正 map 的 key
	return DeleteByName(ctx, tblName, where)
}

// DeleteByName 根据表名删除
//
//	@param ctx
//	@param tblName string 表名
//	@param params map[string]any 删除条件
//	@return int 删除数量
//	@return error 错误
func DeleteByName(ctx context.Context, tblName string, params map[string]any) (int, error) {
	cnt := 0
	err := Transaction(ctx, func(ctx context.Context) (err error) {
		if !strings.Contains(tblName, ".") { // 补充 schema
			tblName = schemaName(ctx) + "." + tblName
		}
		finder := zorm.NewDeleteFinder(fixColumnName(ctx, tblName)) // DELETE FROM t_demo
		for k, v := range params {
			delete(params, k)
			params[fixColumnName(ctx, k)] = v
		}
		finder.Append(`WHERE`).AppendFinder(mapToWhereFinder(params))
		finder.InjectionCheck = false
		finder.GetSQL()
		cnt, err = zorm.UpdateFinder(ctx, finder)
		return
	})
	return cnt, err
}

// QueryMap 执行sql指令进行查询
//
// 用于多表联查,或其他复杂的情况
//
//	@param ctx
//	@param sqlStr string sql进行查询
//	@param values ...any sql中?对应的值
//	@return []map[string]any 结果集
//	@return error 错误
func QueryMap(ctx context.Context, sqlStr string, values ...any) ([]map[string]any, error) {
	fnd := zorm.NewFinder()
	fnd.Append(sqlStr, values...)
	fnd.InjectionCheck = false
	fnd.GetSQL()
	return zorm.QueryMap(ctx, fnd, nil)
}

// ExecuteSql 执行 sql 语句
//
//	@param ctx
//	@param sqlStr string sql进行查询
//	@param values ...any sql中?对应的结果
//	@return int 返回影响的行数
//	@return error 错误
func ExecuteSql(ctx context.Context, sqlStr string, values ...any) (int, error) {
	var cnt int
	err := Transaction(ctx, func(ctx context.Context) (err error) {
		fnd := zorm.NewFinder()
		fnd.Append(sqlStr, values...)
		fnd.InjectionCheck = false
		fnd.GetSQL()
		fnd.InjectionCheck = false
		cnt, err = zorm.UpdateFinder(ctx, fnd)
		return err
	})
	return cnt, err
}
